package com.painter;

/**
 * @code @Author:Painter
 * @code @project_name:Tetris
 * @code @class_name:Main
 * @code @system_login_name:23287
 * @code @time: 2023/4/30  10:03
 */



import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.ArrayList;
import java.util.Collections;

public class Tetris extends JPanel implements ActionListener, KeyListener {


    private final Point[][][] Tetraminos = {
            // I-Point
            {
                    { new Point(0, 1), new Point(1, 1), new Point(2, 1), new Point(3, 1) },
                    { new Point(1, 0), new Point(1, 1), new Point(1, 2), new Point(1, 3) },
                    { new Point(0, 1), new Point(1, 1), new Point(2, 1), new Point(3, 1) },
                    { new Point(1, 0), new Point(1, 1), new Point(1, 2), new Point(1, 3) }
            },
            // J-Point
            {
                    { new Point(0, 1), new Point(1, 1), new Point(2, 1), new Point(2, 0) },
                    { new Point(1, 0), new Point(1, 1), new Point(1, 2), new Point(2, 2) },
                    { new Point(0, 1), new Point(1, 1), new Point(2, 1), new Point(0, 2) },
                    { new Point(1, 0), new Point(1, 1), new Point(1, 2), new Point(0, 0) }
            },
            // L-Point
            {
                    { new Point(0, 1), new Point(1, 1), new Point(2, 1), new Point(2, 2) },
                    { new Point(1, 0), new Point(1, 1), new Point(1, 2), new Point(0, 2) },
                    { new Point(0, 1), new Point(1, 1), new Point(2, 1), new Point(0, 0) },
                    { new Point(1, 0), new Point(1, 1), new Point(1, 2), new Point(2, 0) }
            },
            // O-Point
            {
                    { new Point(0, 0), new Point(0, 1), new Point(1, 0), new Point(1, 1) },
                    { new Point(0, 0), new Point(0, 1), new Point(1, 0), new Point(1, 1) },
                    { new Point(0, 0), new Point(0, 1), new Point(1, 0), new Point(1, 1) },
                    { new Point(0, 0), new Point(0, 1), new Point(1, 0), new Point(1, 1) }
            },
            // S-Point
            {
                    { new Point(1, 0), new Point(2, 0), new Point(0, 1), new Point(1, 1) },
                    { new Point(0, 0), new Point(0, 1), new Point(1, 1), new Point(1, 2) },
                    { new Point(1, 0), new Point(2, 0), new Point(0, 1), new Point(1, 1) },
                    { new Point(0, 0), new Point(0, 1), new Point(1, 1), new Point(1, 2) }
            },
            // T-Point
            {
                    { new Point(1, 0), new Point(0, 1), new Point(1, 1), new Point(2, 1) },
                    { new Point(1, 0), new Point(0, 1), new Point(1, 1), new Point(1, 2) },
                    { new Point(0, 1), new Point(1, 1), new Point(2, 1), new Point(1, 2) },
                    { new Point(1, 0), new Point(1, 1), new Point(2, 1), new Point(1, 2) }
            },
            // Z-Point
            {
                    { new Point(0, 0), new Point(1, 0), new Point(1, 1), new Point(2, 1) },
                    { new Point(1, 1), new Point(0, 2), new Point(1, 2), new Point(0, 3) },
                    { new Point(0, 0), new Point(1, 0), new Point(1, 1), new Point(2, 1) },
                    { new Point(1, 1), new Point(0, 2), new Point(1, 2), new Point(0, 3) }
            }
    };

    private final Color[] tetraminoColors = {
            Color.cyan, Color.blue, Color.orange, Color.yellow, Color.green, Color.pink, Color.red
    };

    private Point PointOrigin;
    private int currentPoint;
    private int rotation;
    private ArrayList<Integer> nextPoints = new ArrayList<Integer>();

    private long score;
    private Color[][] well;




    // Creates a border around the well and initializes the dropping Point
    private void init() {
        well = new Color[12][24];
        for (int i = 0; i < 12; i++) {
            for (int j = 0; j < 23; j++) {
                if (i == 0 || i == 11 || j == 22) {
                    well[i][j] = Color.GRAY;
                } else {
                    well[i][j] = Color.BLACK;
                }
            }
        }
        newPoint();
    }

    // Put a new, random Point into the dropping position
    public void newPoint() {
        PointOrigin = new Point(5, 2);
        rotation = 0;
        if (nextPoints.isEmpty()) {
            Collections.addAll(nextPoints, 0, 1, 2, 3, 4, 5, 6);
            Collections.shuffle(nextPoints);
        }
        currentPoint = nextPoints.get(0);
        nextPoints.remove(0);
    }

    // Collision test for the dropping Point
    private boolean collidesAt(int x, int y, int rotation) {
        for (Point p : Tetraminos[currentPoint][rotation]) {
            if (well[p.x + x][p.y + y] != Color.BLACK) {
                return true;
            }
        }
        return false;
    }

    // Rotate the Point clockwise or counterclockwise
    public void rotate(int i) {
        int newRotation = (rotation + i) % 4;
        if (newRotation < 0) {
            newRotation = 3;
        }
        if (!collidesAt(PointOrigin.x, PointOrigin.y, newRotation)) {
            rotation = newRotation;
        }
        repaint();
    }

    // Move the Point left or right
    public void move(int i) {
        if (!collidesAt(PointOrigin.x + i, PointOrigin.y, rotation)) {
            PointOrigin.x += i;
        }
        repaint();
    }

    // Drops the Point one line or fixes it to the well if it can't drop
    public void dropDown() {
        if (!collidesAt(PointOrigin.x, PointOrigin.y + 1, rotation)) {
            PointOrigin.y += 1;
        } else {
            for (Point p : Tetraminos[currentPoint][rotation]) {
                well[PointOrigin.x + p.x][PointOrigin.y + p.y] = tetraminoColors[currentPoint];
            }
            clearLines();
            newPoint();
        }
        repaint();
    }


    public void deleteRow(int row) {
        for (int j = row-1; j > 0; j--) {
            for (int i = 1; i < 11; i++) {
                well[i][j+1] = well[i][j];
            }
        }
    }

    // Clear completed lines from the field and award score according to
    // the number of simultaneously cleared lines.
    public void clearLines() {
        boolean gap;
        int numClears = 0;

        for (int j = 21; j > 0; j--) {
            gap = false;
            for (int i = 1; i < 11; i++) {
                if (well[i][j] == Color.BLACK) {
                    gap = true;
                    break;
                }
            }
            if (!gap) {
                deleteRow(j);
                j += 1;
                numClears += 1;
            }
        }

        switch (numClears) {
            case 1:
                score += 100;
                break;
            case 2:
                score += 300;
                break;
            case 3:
                score += 500;
                break;
            case 4:
                score += 800;
                break;
        }
    }

    // Draw the falling Point
    private void drawPoint(Graphics g) {
        g.setColor(tetraminoColors[currentPoint]);
        for (Point p : Tetraminos[currentPoint][rotation]) {
            g.fillRect((p.x + PointOrigin.x) * 26,
                    (p.y + PointOrigin.y) * 26,
                    25, 25);
        }
    }

    @Override
    public void paintComponent(Graphics g)
    {
        // Paint the well
        g.fillRect(0, 0, 26*12, 26*23);
        for (int i = 0; i < 12; i++) {
            for (int j = 0; j < 23; j++) {
                g.setColor(well[i][j]);
                g.fillRect(26*i, 26*j, 25, 25);
            }
        }

        // Display the score
        g.setColor(Color.WHITE);
        g.drawString("" + score, 19*12, 25);

        // Draw the currently falling Point
        drawPoint(g);
    }

    public static void main(String[] args) {
        JFrame f = new JFrame("Tetris");
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.setSize(12*25+20, 23*25+60);
        f.setResizable(false);
        f.setVisible(true);

        final Tetris game = new Tetris();
        game.init();
        f.add(game);

        // Keyboard controls
        f.addKeyListener(new KeyListener() {
            public void keyTyped(KeyEvent e) {
            }
            public void keyPressed(KeyEvent e) {
                switch (e.getKeyCode()) {
                    case KeyEvent.VK_UP:
                        game.rotate(-1);
                        break;
                    case KeyEvent.VK_DOWN:
                        game.dropDown();
                        break;
                    case KeyEvent.VK_LEFT:
                        game.move(-1);
                        break;
                    case KeyEvent.VK_RIGHT:
                        game.move(+1);
                        break;
                }
            }
            public void keyReleased(KeyEvent e) {
            }
        });

        // Make the falling Point drop every second
        new Timer(1000, game).start();
    }


    @Override
    public void actionPerformed(ActionEvent e) {
        dropDown();
    }

    @Override
    public void keyTyped(KeyEvent e) {

    }

    @Override
    public void keyPressed(KeyEvent e) {

    }

    @Override
    public void keyReleased(KeyEvent e) {

    }
}
